{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Building an Estimator\n",
    "\n",
    "Once we have our data in the \"right\" format (i.e. files containing\n",
    "`tf.train.Example` protocol buffers), Tensorflow makes the actual\n",
    "ML part straightforward.\n",
    "\n",
    "In this notebook we will first set up the computational graph that\n",
    "reads the input data and transforms it into tensors, and then use\n",
    "Tensorflow's \"canned\" estimator to learn from there.\n",
    "\n",
    "This notebook contains a whole series of exciting bonus sections\n",
    "that you certainly won't have time to solve during the workshop,\n",
    "but you might want to come back to these after the workshop to create\n",
    "more sophisticated models that solve the task better.\n",
    "\n",
    "Table of Contents:\n",
    "\n",
    "- [ 1 Reading the data](#1-Reading-the-data)\n",
    "  - [ 1.1 Reading the data using pure Python](#1.1-Reading-the-data-using-pure-Python)\n",
    "  - [ 1.2 Reading the data using Tensorflow](#1.2-Reading-the-data-using-Tensorflow)\n",
    "- [ 2 Canned estimators](#2-Canned-estimators)\n",
    "- [ 3 Custom convolutional classifier – bonus!](#3-Custom-convolutional-classifier-–-bonus!)\n",
    "- [ 4 Keras – bonus!](#4-Keras-–-bonus!)\n",
    "- [ 5 Recurrent neural network – bonus!](#5-Recurrent-neural-network-–-bonus!)\n",
    "- [A References](#A-References)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.4.1\n"
     ]
    }
   ],
   "source": [
    "# import packages used in this notebook\n",
    "import itertools\n",
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "from matplotlib import pyplot\n",
    "\n",
    "%matplotlib inline\n",
    "# what version of Tensorflow are we running ? should be 1.4.X\n",
    "print tf.__version__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1 Reading the data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "total 365M\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 eval-00000-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 eval-00001-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 eval-00002-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 eval-00003-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 eval-00004-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 eval-00005-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 eval-00006-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 eval-00007-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 eval-00008-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 eval-00009-of-00010\r\n",
      "-rw-r--r-- 1 root root   74 Jan 26 10:41 labels.txt\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 test-00000-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 test-00001-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 test-00002-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 test-00003-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 test-00004-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 test-00005-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 test-00006-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 test-00007-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 test-00008-of-00010\r\n",
      "-rw-r--r-- 1 root root 5.3M Jan 26 10:41 test-00009-of-00010\r\n",
      "-rw-r--r-- 1 root root  27M Jan 26 10:41 train-00000-of-00010\r\n",
      "-rw-r--r-- 1 root root  27M Jan 26 10:41 train-00001-of-00010\r\n",
      "-rw-r--r-- 1 root root  27M Jan 26 10:41 train-00002-of-00010\r\n",
      "-rw-r--r-- 1 root root  27M Jan 26 10:41 train-00003-of-00010\r\n",
      "-rw-r--r-- 1 root root  27M Jan 26 10:41 train-00004-of-00010\r\n",
      "-rw-r--r-- 1 root root  27M Jan 26 10:41 train-00005-of-00010\r\n",
      "-rw-r--r-- 1 root root  27M Jan 26 10:41 train-00006-of-00010\r\n",
      "-rw-r--r-- 1 root root  27M Jan 26 10:41 train-00007-of-00010\r\n",
      "-rw-r--r-- 1 root root  27M Jan 26 10:41 train-00008-of-00010\r\n",
      "-rw-r--r-- 1 root root  27M Jan 26 10:41 train-00009-of-00010\r\n"
     ]
    }
   ],
   "source": [
    "# Load data generated in 1_qd_data:\n",
    "data_path = '../data/dataset_img'\n",
    "# Show files in dataset.\n",
    "!ls -l -h $data_path"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10 label classes:\n",
      "\n",
      "0 -> elephant\n",
      "1 -> giraffe\n",
      "2 -> kangaroo\n",
      "3 -> lion\n",
      "4 -> monkey\n",
      "5 -> panda\n",
      "6 -> penguin\n",
      "7 -> rhinoceros\n",
      "8 -> tiger\n",
      "9 -> zebra\n"
     ]
    }
   ],
   "source": [
    "# It's customary to identify the label by an integer number. These\n",
    "# numbers map to the different classes as follows:\n",
    "classes = open('%s/labels.txt' % data_path).read().splitlines()\n",
    "print '%d label classes:\\n' % len(classes)\n",
    "for i, label in enumerate(classes):\n",
    "    print '%d -> %s' % (i, label)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.1 Reading the data using pure Python"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "           timestamp (int64_list)\n",
      "              key_id (int64_list)\n",
      "          recognized (int64_list)\n",
      "              img_64 (int64_list)\n",
      "         countrycode (bytes_list)\n",
      "                word (bytes_list)\n",
      "               label (int64_list)\n"
     ]
    }
   ],
   "source": [
    "# Step 1 : Read a single record from the first file.\n",
    "train_files = tf.gfile.Glob('%s/train-*' % data_path)\n",
    "record = tf.python_io.tf_record_iterator(train_files[0]).next()\n",
    "# Step 2 : Parse record into tf.train.Example proto.\n",
    "example = tf.train.Example.FromString(record)\n",
    "# We need the image \"img_64\" and the \"label\". Both are of type\n",
    "# int64_list: the label is stored by its index and the image is\n",
    "# a 64x64 list of integers that are the pixel values.\n",
    "for name, feature in example.features.feature.items():\n",
    "    print '%20s (%s)' % (name, feature.WhichOneof('kind'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Step 3 : Read features.\n",
    "img_64 = example.features.feature['img_64'].int64_list.value\n",
    "img_64 = np.array(img_64).reshape((64, 64)) / 255.\n",
    "label = example.features.feature['label'].int64_list.value[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f7a29fc6f10>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Visualize drawing.\n",
    "\n",
    "def show_img(img_64, title, ax=None):\n",
    "    (ax if ax else pyplot).matshow(img_64, cmap='gray')\n",
    "    ax = ax if ax else pyplot.gca()\n",
    "    ax.set_xticks([])\n",
    "    ax.set_yticks([])\n",
    "    ax.set_title(title)\n",
    "\n",
    "show_img(img_64, classes[label])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.2 Reading the data using Tensorflow"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing _derived/4_input_fn_img.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile _derived/4_input_fn_img.py\n",
    "# (Written into separate file for sharing with cloud code.)\n",
    "\n",
    "# Read input data from sharded files using 1.4 API.\n",
    "# This is copied and slightly simplified from tf_snippets.ipynb\n",
    "\n",
    "# Note that this cell simply defines the functions but does not yet call\n",
    "# them. The functions are executed in the next cell within a tf.Graph()\n",
    "# context.\n",
    "\n",
    "# This dictionary specifies what \"features\" we want to extract from the\n",
    "# tf.train.Example protos (i.e. what they look like on disk). We only\n",
    "# need the image data \"img_64\" and the \"label\". Both features are tensors\n",
    "# with a fixed length.\n",
    "# You need to specify the correct \"shape\" and \"dtype\" parameters for\n",
    "# these features...\n",
    "feature_spec = {\n",
    "    # Single label per example => shape=[1] (we could also use shape=() and\n",
    "    # then do a transformation in the input_fn).\n",
    "    'label': tf.FixedLenFeature(shape=[1], dtype=tf.int64),\n",
    "    # The bytes_list data is parsed into tf.string.\n",
    "    'img_64': tf.FixedLenFeature(shape=[64, 64], dtype=tf.int64),\n",
    "}\n",
    "\n",
    "def parse_example(serialized_example):\n",
    "    # Convert string to tf.train.Example and then extract features/label.\n",
    "    features = tf.parse_single_example(serialized_example, feature_spec)\n",
    "    # Important step: remove \"label\" from features!\n",
    "    # Otherwise our classifier would simply learn to predict\n",
    "    # label=features['label']...\n",
    "    label = features.pop('label')\n",
    "    # Convert int64 [0..255] to float [0..1]\n",
    "    features['img_64'] = tf.cast(features['img_64'], tf.float32) / 255.\n",
    "    return features, label\n",
    "\n",
    "# Common Tensorflow pattern : wrap function to specify parameters.\n",
    "# The estimator interface expects a \"input_fn\" as a parameter, and not the\n",
    "# tensors (features, labels) so it can re-create the tensors in different\n",
    "# graphs later on.\n",
    "def make_input_fn(files_pattern, batch_size=100):\n",
    "    def input_fn():\n",
    "        # Signature input_fn: () -> features=Dict[str, Tensor], labels=Tensor\n",
    "        ds = tf.data.TFRecordDataset(tf.gfile.Glob(files_pattern))\n",
    "        ds = ds.map(parse_example).batch(batch_size)\n",
    "        ds = ds.shuffle(buffer_size=5*batch_size).repeat()\n",
    "        features, labels = ds.make_one_shot_iterator().get_next()\n",
    "        return features, labels\n",
    "    return input_fn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f7a4ff8a090>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Define a graph, load a batch of examples and display one of them.\n",
    "\n",
    "%run -i _derived/4_input_fn_img.py\n",
    "\n",
    "# First, we create the input_fn -- this defines which files to read from\n",
    "# and the batch size, and can later be passed around.\n",
    "batch_size = 5\n",
    "input_fn = make_input_fn(files_pattern='%s/train-*' % data_path,\n",
    "                         batch_size=batch_size)\n",
    "with tf.Graph().as_default():\n",
    "    # After registering a default graph, we call the previously defined\n",
    "    # input_fn.\n",
    "    # This yields the dictionary of feature tensors and the label tensor.\n",
    "    features, labels = input_fn()\n",
    "    with tf.train.MonitoredSession() as sess:\n",
    "        # Note: We create a tf.train.MonitoredSession instead of a normal\n",
    "        # tf.Session because Tensorflow is using pipelines under the hood\n",
    "        # to read data in a streaming fashion from disk into our computational\n",
    "        # Graph. If we used a plain old tf.Session(), then we would have to\n",
    "        # take care of initializing the threads for the pipelines ourselves...\n",
    "        img_64_, labels_ = sess.run([features['img_64'], labels])\n",
    "        # Note that first dimension is the batch dimension.\n",
    "        for i in range(batch_size):\n",
    "            ax = pyplot.subplot(1, batch_size, i+1)\n",
    "            show_img(img_64_[i], classes[labels_[i][0]], ax)\n",
    "\n",
    "# Also note how content changes upon every re-execution of this cell.\n",
    "# That is because of the randomization in the input_fn."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2 Canned estimators\n",
    "\n",
    "Once we have our `input_fn`, it's a piece of cake to do some\n",
    "machine learning on top of it using Tensorflow's \"canned\n",
    "estimators\". Let's start with a simple\n",
    "[LinearClassifier](https://www.tensorflow.org/versions/master/api_docs/python/tf/estimator/LinearClassifier).\n",
    "\n",
    "A LinearClassifier implements simple **logistic regression** that is basically\n",
    "a neural network where every input neuron (pixel image) is directly connected\n",
    "to all output neurons (probabilities for different classes). Every output neuron\n",
    "effectively remembers a \"mask\" in the input pixel space, an approach that\n",
    "quickly learns to identify drawings that have similar pixel activations, but\n",
    "fails to learn anything more complicated (e.g. an elephant's trunk could\n",
    "activate the giraffe's \"neck mask\" if it happened to be in the same pixel\n",
    "region)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Using default config.\n",
      "WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmpylAuZK\n",
      "INFO:tensorflow:Using config: {'_save_checkpoints_secs': 600, '_session_config': None, '_keep_checkpoint_max': 5, '_task_type': 'worker', '_is_chief': True, '_cluster_spec': <tensorflow.python.training.server_lib.ClusterSpec object at 0x7f7a29f78390>, '_save_checkpoints_steps': None, '_keep_checkpoint_every_n_hours': 10000, '_service': None, '_num_ps_replicas': 0, '_tf_random_seed': None, '_master': '', '_num_worker_replicas': 1, '_task_id': 0, '_log_step_count_steps': 100, '_model_dir': '/tmp/tmpylAuZK', '_save_summary_steps': 100}\n"
     ]
    }
   ],
   "source": [
    "# \"Feature columns\" tell our canned estimator how to make use of the\n",
    "# data returned by input_fn(). In our case we have a single feature that\n",
    "# is of numerical (defaults to tf.float32) type with shape=(64, 64).\n",
    "feature_columns = [\n",
    "    # Add a feature column of numeric type and correct shape for the \"img_64\" feature.\n",
    "    tf.feature_column.numeric_column('img_64', shape=(64, 64))\n",
    "]\n",
    "\n",
    "# Note that feature columns can also be used to generate the \"feature_spec\"\n",
    "# used to parsing the tf.train.Example protocol buffers, and that there are\n",
    "# other feature column types that can do complicated data transformation\n",
    "# (such as looking up strings and creating embeddings) -- but that's a story\n",
    "# for another workshop ;-)\n",
    "\n",
    "# (we also need to specify number of output classes)\n",
    "linear_estimator  = tf.estimator.LinearClassifier(feature_columns=feature_columns,\n",
    "                                                  n_classes=len(classes))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Create CheckpointSaverHook.\n",
      "INFO:tensorflow:Saving checkpoints for 1 into /tmp/tmpylAuZK/model.ckpt.\n",
      "INFO:tensorflow:loss = 230.259, step = 1\n",
      "INFO:tensorflow:global_step/sec: 279.083\n",
      "INFO:tensorflow:loss = 1150.84, step = 101 (0.359 sec)\n",
      "INFO:tensorflow:global_step/sec: 323.898\n",
      "INFO:tensorflow:loss = 439.281, step = 201 (0.310 sec)\n",
      "INFO:tensorflow:Saving checkpoints for 300 into /tmp/tmpylAuZK/model.ckpt.\n",
      "INFO:tensorflow:Loss for final step: 666.167.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.estimator.canned.linear.LinearClassifier at 0x7f7a29fc6a10>"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Connect it to the input_fn with the training data and train the classifier.\n",
    "\n",
    "### YOUR ACTION REQUIRED:\n",
    "# You have to specify what data to use for training (which input_fn), and how\n",
    "# many steps you want to train for. The number of examples used for training is\n",
    "# = steps * batch_size.\n",
    "input_fn = make_input_fn('%s/train-*' % data_path) #input_fn = make_input_fn(...)\n",
    "\n",
    "### YOUR ACTION REQUIRED:\n",
    "# Try different number of training steps and see how this influences training time\n",
    "# (and accuracy in cell below). Note that you have to re-run the previous cell\n",
    "# to \"reset\" the classifier (otherwise the estimator will simply *continue* the\n",
    "# training with an additional number of steps).\n",
    "steps = 300 #steps =\n",
    "linear_estimator.train(input_fn=input_fn, steps=steps)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Starting evaluation at 2018-01-26-10:44:25\n",
      "INFO:tensorflow:Restoring parameters from /tmp/tmpylAuZK/model.ckpt-300\n",
      "INFO:tensorflow:Evaluation [1/100]\n",
      "INFO:tensorflow:Evaluation [2/100]\n",
      "INFO:tensorflow:Evaluation [3/100]\n",
      "INFO:tensorflow:Evaluation [4/100]\n",
      "INFO:tensorflow:Evaluation [5/100]\n",
      "INFO:tensorflow:Evaluation [6/100]\n",
      "INFO:tensorflow:Evaluation [7/100]\n",
      "INFO:tensorflow:Evaluation [8/100]\n",
      "INFO:tensorflow:Evaluation [9/100]\n",
      "INFO:tensorflow:Evaluation [10/100]\n",
      "INFO:tensorflow:Evaluation [11/100]\n",
      "INFO:tensorflow:Evaluation [12/100]\n",
      "INFO:tensorflow:Evaluation [13/100]\n",
      "INFO:tensorflow:Evaluation [14/100]\n",
      "INFO:tensorflow:Evaluation [15/100]\n",
      "INFO:tensorflow:Evaluation [16/100]\n",
      "INFO:tensorflow:Evaluation [17/100]\n",
      "INFO:tensorflow:Evaluation [18/100]\n",
      "INFO:tensorflow:Evaluation [19/100]\n",
      "INFO:tensorflow:Evaluation [20/100]\n",
      "INFO:tensorflow:Evaluation [21/100]\n",
      "INFO:tensorflow:Evaluation [22/100]\n",
      "INFO:tensorflow:Evaluation [23/100]\n",
      "INFO:tensorflow:Evaluation [24/100]\n",
      "INFO:tensorflow:Evaluation [25/100]\n",
      "INFO:tensorflow:Evaluation [26/100]\n",
      "INFO:tensorflow:Evaluation [27/100]\n",
      "INFO:tensorflow:Evaluation [28/100]\n",
      "INFO:tensorflow:Evaluation [29/100]\n",
      "INFO:tensorflow:Evaluation [30/100]\n",
      "INFO:tensorflow:Evaluation [31/100]\n",
      "INFO:tensorflow:Evaluation [32/100]\n",
      "INFO:tensorflow:Evaluation [33/100]\n",
      "INFO:tensorflow:Evaluation [34/100]\n",
      "INFO:tensorflow:Evaluation [35/100]\n",
      "INFO:tensorflow:Evaluation [36/100]\n",
      "INFO:tensorflow:Evaluation [37/100]\n",
      "INFO:tensorflow:Evaluation [38/100]\n",
      "INFO:tensorflow:Evaluation [39/100]\n",
      "INFO:tensorflow:Evaluation [40/100]\n",
      "INFO:tensorflow:Evaluation [41/100]\n",
      "INFO:tensorflow:Evaluation [42/100]\n",
      "INFO:tensorflow:Evaluation [43/100]\n",
      "INFO:tensorflow:Evaluation [44/100]\n",
      "INFO:tensorflow:Evaluation [45/100]\n",
      "INFO:tensorflow:Evaluation [46/100]\n",
      "INFO:tensorflow:Evaluation [47/100]\n",
      "INFO:tensorflow:Evaluation [48/100]\n",
      "INFO:tensorflow:Evaluation [49/100]\n",
      "INFO:tensorflow:Evaluation [50/100]\n",
      "INFO:tensorflow:Evaluation [51/100]\n",
      "INFO:tensorflow:Evaluation [52/100]\n",
      "INFO:tensorflow:Evaluation [53/100]\n",
      "INFO:tensorflow:Evaluation [54/100]\n",
      "INFO:tensorflow:Evaluation [55/100]\n",
      "INFO:tensorflow:Evaluation [56/100]\n",
      "INFO:tensorflow:Evaluation [57/100]\n",
      "INFO:tensorflow:Evaluation [58/100]\n",
      "INFO:tensorflow:Evaluation [59/100]\n",
      "INFO:tensorflow:Evaluation [60/100]\n",
      "INFO:tensorflow:Evaluation [61/100]\n",
      "INFO:tensorflow:Evaluation [62/100]\n",
      "INFO:tensorflow:Evaluation [63/100]\n",
      "INFO:tensorflow:Evaluation [64/100]\n",
      "INFO:tensorflow:Evaluation [65/100]\n",
      "INFO:tensorflow:Evaluation [66/100]\n",
      "INFO:tensorflow:Evaluation [67/100]\n",
      "INFO:tensorflow:Evaluation [68/100]\n",
      "INFO:tensorflow:Evaluation [69/100]\n",
      "INFO:tensorflow:Evaluation [70/100]\n",
      "INFO:tensorflow:Evaluation [71/100]\n",
      "INFO:tensorflow:Evaluation [72/100]\n",
      "INFO:tensorflow:Evaluation [73/100]\n",
      "INFO:tensorflow:Evaluation [74/100]\n",
      "INFO:tensorflow:Evaluation [75/100]\n",
      "INFO:tensorflow:Evaluation [76/100]\n",
      "INFO:tensorflow:Evaluation [77/100]\n",
      "INFO:tensorflow:Evaluation [78/100]\n",
      "INFO:tensorflow:Evaluation [79/100]\n",
      "INFO:tensorflow:Evaluation [80/100]\n",
      "INFO:tensorflow:Evaluation [81/100]\n",
      "INFO:tensorflow:Evaluation [82/100]\n",
      "INFO:tensorflow:Evaluation [83/100]\n",
      "INFO:tensorflow:Evaluation [84/100]\n",
      "INFO:tensorflow:Evaluation [85/100]\n",
      "INFO:tensorflow:Evaluation [86/100]\n",
      "INFO:tensorflow:Evaluation [87/100]\n",
      "INFO:tensorflow:Evaluation [88/100]\n",
      "INFO:tensorflow:Evaluation [89/100]\n",
      "INFO:tensorflow:Evaluation [90/100]\n",
      "INFO:tensorflow:Evaluation [91/100]\n",
      "INFO:tensorflow:Evaluation [92/100]\n",
      "INFO:tensorflow:Evaluation [93/100]\n",
      "INFO:tensorflow:Evaluation [94/100]\n",
      "INFO:tensorflow:Evaluation [95/100]\n",
      "INFO:tensorflow:Evaluation [96/100]\n",
      "INFO:tensorflow:Evaluation [97/100]\n",
      "INFO:tensorflow:Evaluation [98/100]\n",
      "INFO:tensorflow:Evaluation [99/100]\n",
      "INFO:tensorflow:Evaluation [100/100]\n",
      "INFO:tensorflow:Finished evaluation at 2018-01-26-10:44:28\n",
      "INFO:tensorflow:Saving dict for global step 300: accuracy = 0.5477, average_loss = 5.75172, global_step = 300, loss = 575.172\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'accuracy': 0.54769999,\n",
       " 'average_loss': 5.7517166,\n",
       " 'global_step': 300,\n",
       " 'loss': 575.17169}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Evaluate the fitted classifier.\n",
    "### YOUR ACTION REQUIRED:\n",
    "# Find the correct values for below variables. Note that you should *not*\n",
    "# evaluate with the same data used for training.\n",
    "input_fn = make_input_fn('%s/eval-*' % data_path) #input_fn = \n",
    "steps = 100 #steps =\n",
    "linear_estimator.evaluate(input_fn=input_fn, steps=steps)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Restoring parameters from /tmp/tmpylAuZK/model.ckpt-300\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f7a4fc2eb90>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# The simple LinearClassifier does not perform too well... Let's examine\n",
    "# some classifications so we can see which cases were failing.\n",
    "\n",
    "# Because estimator.predict() only returns class probabilities and not the\n",
    "# input data, we parse the features in Python (which gives us a handle on\n",
    "# the input data) and then create an input_fn using numpy_input_fn().\n",
    "\n",
    "def get_label_img(files_pattern, n):\n",
    "    \"\"\"Reads batch of \"img_64\" and \"label\" features.\n",
    "\n",
    "    Args:\n",
    "      files_pattern: Pattern matching files containing tf.train.Example.\n",
    "      n: Number of examples to parse.\n",
    "\n",
    "    Returns:\n",
    "      (imgs, labels) with shapes (n, 64, 64) and (n, 1). The image pixel values\n",
    "      fall in the range 0..1.\n",
    "    \"\"\"\n",
    "    imgs = np.zeros((n, 64, 64), dtype=np.float32)\n",
    "    labels = np.zeros((n, 1), dtype=np.int64)\n",
    "    i = 0\n",
    "    for filename in  tf.gfile.Glob(files_pattern):\n",
    "        for record in tf.python_io.tf_record_iterator(filename):\n",
    "            example = tf.train.Example.FromString(record)\n",
    "            labels[i, 0] = example.features.feature['label'].int64_list.value[0]\n",
    "            img_64 = example.features.feature['img_64'].int64_list.value\n",
    "            imgs[i, :, :] = np.array(img_64).reshape((64, 64)) / 255.\n",
    "            i += 1\n",
    "            if i >= n:\n",
    "                return imgs, labels\n",
    "\n",
    "def show_predictions(estimator, rows=4, cols=4, predict_keys=None):\n",
    "    \"\"\"Shows predictions + labels for a couple of examples.\"\"\"\n",
    "    # Read data in Python.\n",
    "    imgs, labels = get_label_img('%s/test-*' % data_path, n=rows*cols)\n",
    "    # Create input_fn from Python data.\n",
    "    input_fn = tf.estimator.inputs.numpy_input_fn({'img_64': imgs}, shuffle=False)\n",
    "    # Get predictions.\n",
    "    predict_iter = estimator.predict(input_fn=input_fn, predict_keys=predict_keys)\n",
    "    _, axs = pyplot.subplots(rows, cols, figsize=(cols*3, rows*3))\n",
    "    for (i, prediction) in enumerate(predict_iter):\n",
    "        # Plot image, predicted label + correct label.\n",
    "        predicted = np.argmax(prediction['probabilities'])\n",
    "        label = labels[i, 0]\n",
    "        title = '%s? %s!' % (classes[predicted], classes[label])\n",
    "        ax = axs[i//cols][i%cols]\n",
    "        show_img(imgs[i], title, ax)\n",
    "        ax.title.set_position([0.5, 1.0])\n",
    "\n",
    "show_predictions(linear_estimator)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Using default config.\n",
      "WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmpwAUNkw\n",
      "INFO:tensorflow:Using config: {'_save_checkpoints_secs': 600, '_session_config': None, '_keep_checkpoint_max': 5, '_task_type': 'worker', '_is_chief': True, '_cluster_spec': <tensorflow.python.training.server_lib.ClusterSpec object at 0x7f7a4feae290>, '_save_checkpoints_steps': None, '_keep_checkpoint_every_n_hours': 10000, '_service': None, '_num_ps_replicas': 0, '_tf_random_seed': None, '_master': '', '_num_worker_replicas': 1, '_task_id': 0, '_log_step_count_steps': 100, '_model_dir': '/tmp/tmpwAUNkw', '_save_summary_steps': 100}\n",
      "INFO:tensorflow:Create CheckpointSaverHook.\n",
      "INFO:tensorflow:Saving checkpoints for 1 into /tmp/tmpwAUNkw/model.ckpt.\n",
      "INFO:tensorflow:loss = 250.297, step = 1\n",
      "INFO:tensorflow:global_step/sec: 21.2043\n",
      "INFO:tensorflow:loss = 113.546, step = 101 (4.718 sec)\n",
      "INFO:tensorflow:global_step/sec: 21.5706\n",
      "INFO:tensorflow:loss = 104.84, step = 201 (4.638 sec)\n",
      "INFO:tensorflow:global_step/sec: 21.6409\n",
      "INFO:tensorflow:loss = 106.013, step = 301 (4.621 sec)\n",
      "INFO:tensorflow:global_step/sec: 21.0926\n",
      "INFO:tensorflow:loss = 78.5747, step = 401 (4.739 sec)\n",
      "INFO:tensorflow:global_step/sec: 6.64521\n",
      "INFO:tensorflow:loss = 101.189, step = 501 (15.048 sec)\n",
      "INFO:tensorflow:global_step/sec: 23.4216\n",
      "INFO:tensorflow:loss = 88.1858, step = 601 (4.269 sec)\n",
      "INFO:tensorflow:global_step/sec: 22.9545\n",
      "INFO:tensorflow:loss = 85.1741, step = 701 (4.357 sec)\n",
      "INFO:tensorflow:global_step/sec: 23.6657\n",
      "INFO:tensorflow:loss = 112.618, step = 801 (4.224 sec)\n",
      "INFO:tensorflow:global_step/sec: 23.4752\n",
      "INFO:tensorflow:loss = 70.838, step = 901 (4.260 sec)\n",
      "INFO:tensorflow:global_step/sec: 6.37205\n",
      "INFO:tensorflow:loss = 81.9654, step = 1001 (15.693 sec)\n",
      "INFO:tensorflow:global_step/sec: 23.3307\n",
      "INFO:tensorflow:loss = 85.6062, step = 1101 (4.286 sec)\n",
      "INFO:tensorflow:global_step/sec: 23.8442\n",
      "INFO:tensorflow:loss = 74.5622, step = 1201 (4.194 sec)\n",
      "INFO:tensorflow:global_step/sec: 23.8715\n",
      "INFO:tensorflow:loss = 82.7008, step = 1301 (4.190 sec)\n",
      "INFO:tensorflow:global_step/sec: 22.7415\n",
      "INFO:tensorflow:loss = 69.9822, step = 1401 (4.397 sec)\n",
      "INFO:tensorflow:global_step/sec: 6.40488\n",
      "INFO:tensorflow:loss = 74.6891, step = 1501 (15.613 sec)\n",
      "INFO:tensorflow:global_step/sec: 25.3621\n",
      "INFO:tensorflow:loss = 67.3087, step = 1601 (3.944 sec)\n",
      "INFO:tensorflow:global_step/sec: 24.9102\n",
      "INFO:tensorflow:loss = 76.9414, step = 1701 (4.013 sec)\n",
      "INFO:tensorflow:global_step/sec: 24.8875\n",
      "INFO:tensorflow:loss = 75.7889, step = 1801 (4.019 sec)\n",
      "INFO:tensorflow:global_step/sec: 23.5761\n",
      "INFO:tensorflow:loss = 84.3875, step = 1901 (4.243 sec)\n",
      "INFO:tensorflow:global_step/sec: 6.70952\n",
      "INFO:tensorflow:loss = 72.0065, step = 2001 (14.903 sec)\n",
      "INFO:tensorflow:global_step/sec: 25.5364\n",
      "INFO:tensorflow:loss = 61.5739, step = 2101 (3.916 sec)\n",
      "INFO:tensorflow:global_step/sec: 25.5675\n",
      "INFO:tensorflow:loss = 61.4231, step = 2201 (3.912 sec)\n",
      "INFO:tensorflow:global_step/sec: 24.0594\n",
      "INFO:tensorflow:loss = 63.1738, step = 2301 (4.157 sec)\n",
      "INFO:tensorflow:global_step/sec: 23.8158\n",
      "INFO:tensorflow:loss = 69.6621, step = 2401 (4.198 sec)\n",
      "INFO:tensorflow:global_step/sec: 7.67949\n",
      "INFO:tensorflow:loss = 56.2631, step = 2501 (13.023 sec)\n",
      "INFO:tensorflow:global_step/sec: 28.1916\n",
      "INFO:tensorflow:loss = 76.5501, step = 2601 (3.546 sec)\n",
      "INFO:tensorflow:global_step/sec: 28.3268\n",
      "INFO:tensorflow:loss = 58.623, step = 2701 (3.530 sec)\n",
      "INFO:tensorflow:global_step/sec: 27.9049\n",
      "INFO:tensorflow:loss = 54.3795, step = 2801 (3.584 sec)\n",
      "INFO:tensorflow:global_step/sec: 28.2655\n",
      "INFO:tensorflow:loss = 55.6658, step = 2901 (3.538 sec)\n",
      "INFO:tensorflow:Saving checkpoints for 3000 into /tmp/tmpwAUNkw/model.ckpt.\n",
      "INFO:tensorflow:Loss for final step: 64.2881.\n",
      "INFO:tensorflow:Starting evaluation at 2018-01-26-10:47:40\n",
      "INFO:tensorflow:Restoring parameters from /tmp/tmpwAUNkw/model.ckpt-3000\n",
      "INFO:tensorflow:Evaluation [1/100]\n",
      "INFO:tensorflow:Evaluation [2/100]\n",
      "INFO:tensorflow:Evaluation [3/100]\n",
      "INFO:tensorflow:Evaluation [4/100]\n",
      "INFO:tensorflow:Evaluation [5/100]\n",
      "INFO:tensorflow:Evaluation [6/100]\n",
      "INFO:tensorflow:Evaluation [7/100]\n",
      "INFO:tensorflow:Evaluation [8/100]\n",
      "INFO:tensorflow:Evaluation [9/100]\n",
      "INFO:tensorflow:Evaluation [10/100]\n",
      "INFO:tensorflow:Evaluation [11/100]\n",
      "INFO:tensorflow:Evaluation [12/100]\n",
      "INFO:tensorflow:Evaluation [13/100]\n",
      "INFO:tensorflow:Evaluation [14/100]\n",
      "INFO:tensorflow:Evaluation [15/100]\n",
      "INFO:tensorflow:Evaluation [16/100]\n",
      "INFO:tensorflow:Evaluation [17/100]\n",
      "INFO:tensorflow:Evaluation [18/100]\n",
      "INFO:tensorflow:Evaluation [19/100]\n",
      "INFO:tensorflow:Evaluation [20/100]\n",
      "INFO:tensorflow:Evaluation [21/100]\n",
      "INFO:tensorflow:Evaluation [22/100]\n",
      "INFO:tensorflow:Evaluation [23/100]\n",
      "INFO:tensorflow:Evaluation [24/100]\n",
      "INFO:tensorflow:Evaluation [25/100]\n",
      "INFO:tensorflow:Evaluation [26/100]\n",
      "INFO:tensorflow:Evaluation [27/100]\n",
      "INFO:tensorflow:Evaluation [28/100]\n",
      "INFO:tensorflow:Evaluation [29/100]\n",
      "INFO:tensorflow:Evaluation [30/100]\n",
      "INFO:tensorflow:Evaluation [31/100]\n",
      "INFO:tensorflow:Evaluation [32/100]\n",
      "INFO:tensorflow:Evaluation [33/100]\n",
      "INFO:tensorflow:Evaluation [34/100]\n",
      "INFO:tensorflow:Evaluation [35/100]\n",
      "INFO:tensorflow:Evaluation [36/100]\n",
      "INFO:tensorflow:Evaluation [37/100]\n",
      "INFO:tensorflow:Evaluation [38/100]\n",
      "INFO:tensorflow:Evaluation [39/100]\n",
      "INFO:tensorflow:Evaluation [40/100]\n",
      "INFO:tensorflow:Evaluation [41/100]\n",
      "INFO:tensorflow:Evaluation [42/100]\n",
      "INFO:tensorflow:Evaluation [43/100]\n",
      "INFO:tensorflow:Evaluation [44/100]\n",
      "INFO:tensorflow:Evaluation [45/100]\n",
      "INFO:tensorflow:Evaluation [46/100]\n",
      "INFO:tensorflow:Evaluation [47/100]\n",
      "INFO:tensorflow:Evaluation [48/100]\n",
      "INFO:tensorflow:Evaluation [49/100]\n",
      "INFO:tensorflow:Evaluation [50/100]\n",
      "INFO:tensorflow:Evaluation [51/100]\n",
      "INFO:tensorflow:Evaluation [52/100]\n",
      "INFO:tensorflow:Evaluation [53/100]\n",
      "INFO:tensorflow:Evaluation [54/100]\n",
      "INFO:tensorflow:Evaluation [55/100]\n",
      "INFO:tensorflow:Evaluation [56/100]\n",
      "INFO:tensorflow:Evaluation [57/100]\n",
      "INFO:tensorflow:Evaluation [58/100]\n",
      "INFO:tensorflow:Evaluation [59/100]\n",
      "INFO:tensorflow:Evaluation [60/100]\n",
      "INFO:tensorflow:Evaluation [61/100]\n",
      "INFO:tensorflow:Evaluation [62/100]\n",
      "INFO:tensorflow:Evaluation [63/100]\n",
      "INFO:tensorflow:Evaluation [64/100]\n",
      "INFO:tensorflow:Evaluation [65/100]\n",
      "INFO:tensorflow:Evaluation [66/100]\n",
      "INFO:tensorflow:Evaluation [67/100]\n",
      "INFO:tensorflow:Evaluation [68/100]\n",
      "INFO:tensorflow:Evaluation [69/100]\n",
      "INFO:tensorflow:Evaluation [70/100]\n",
      "INFO:tensorflow:Evaluation [71/100]\n",
      "INFO:tensorflow:Evaluation [72/100]\n",
      "INFO:tensorflow:Evaluation [73/100]\n",
      "INFO:tensorflow:Evaluation [74/100]\n",
      "INFO:tensorflow:Evaluation [75/100]\n",
      "INFO:tensorflow:Evaluation [76/100]\n",
      "INFO:tensorflow:Evaluation [77/100]\n",
      "INFO:tensorflow:Evaluation [78/100]\n",
      "INFO:tensorflow:Evaluation [79/100]\n",
      "INFO:tensorflow:Evaluation [80/100]\n",
      "INFO:tensorflow:Evaluation [81/100]\n",
      "INFO:tensorflow:Evaluation [82/100]\n",
      "INFO:tensorflow:Evaluation [83/100]\n",
      "INFO:tensorflow:Evaluation [84/100]\n",
      "INFO:tensorflow:Evaluation [85/100]\n",
      "INFO:tensorflow:Evaluation [86/100]\n",
      "INFO:tensorflow:Evaluation [87/100]\n",
      "INFO:tensorflow:Evaluation [88/100]\n",
      "INFO:tensorflow:Evaluation [89/100]\n",
      "INFO:tensorflow:Evaluation [90/100]\n",
      "INFO:tensorflow:Evaluation [91/100]\n",
      "INFO:tensorflow:Evaluation [92/100]\n",
      "INFO:tensorflow:Evaluation [93/100]\n",
      "INFO:tensorflow:Evaluation [94/100]\n",
      "INFO:tensorflow:Evaluation [95/100]\n",
      "INFO:tensorflow:Evaluation [96/100]\n",
      "INFO:tensorflow:Evaluation [97/100]\n",
      "INFO:tensorflow:Evaluation [98/100]\n",
      "INFO:tensorflow:Evaluation [99/100]\n",
      "INFO:tensorflow:Evaluation [100/100]\n",
      "INFO:tensorflow:Finished evaluation at 2018-01-26-10:47:43\n",
      "INFO:tensorflow:Saving dict for global step 3000: accuracy = 0.7129, average_loss = 0.864552, global_step = 3000, loss = 86.4552\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'accuracy': 0.71289998,\n",
       " 'average_loss': 0.86455178,\n",
       " 'global_step': 3000,\n",
       " 'loss': 86.455177}"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Let's use another canned estimator : The \"DNNClassifier\" (where DNN stands\n",
    "# for \"deep neural network\"). Since we're using the same estimator interface,\n",
    "# we can use the same input_fn() as before.\n",
    "\n",
    "### YOUR ACTION REQUIRED:\n",
    "# Try to find a configuration that outperforms our linear classifier.\n",
    "steps = 3000 #steps = \n",
    "hidden_units = [1000] #hidden_units = \n",
    "\n",
    "dnn_estimator  = tf.estimator.DNNClassifier(\n",
    "    hidden_units=hidden_units,\n",
    "    feature_columns=feature_columns,\n",
    "    n_classes=len(classes))\n",
    "dnn_estimator.train(input_fn=make_input_fn('%s/train-*' % data_path),\n",
    "                    steps=steps)\n",
    "dnn_estimator.evaluate(input_fn=make_input_fn('%s/eval-*' % data_path),\n",
    "                       steps=100)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3 Custom convolutional classifier – bonus!<a name=\"_3 custom convolutional classifier – bonus!\"></a><a name=\"_3 custom convolutional classifier – bonus!\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the previous section we used a \"canned\" linear estimator that\n",
    "computed the predictions from the pixel values directly by logistic\n",
    "regression.\n",
    "\n",
    "Of course we know that the pixel values are not randomly distributed,\n",
    "but actually form a two-dimensional image and we can leverage our\n",
    "understanding of the data by using\n",
    "[2D convolutions](https://en.wikipedia.org/wiki/Multidimensional_discrete_convolution).\n",
    "\n",
    "We will still be using the `tf.estimator` interface, but this time we\n",
    "specify the computational graph that computes the predictions from the\n",
    "raw pixel values by hand, using 2D convolutions and max pooling (max pooling\n",
    "is used to reduce the convoluted image's spatial dimensions)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting _derived/4_get_logits_img.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile _derived/4_get_logits_img.py\n",
    "# (Written into separate file for sharing with cloud code.)\n",
    "\n",
    "# Define a function that computes \"logits\" from features.\n",
    "# The \"logits\" are unbound numbers that will be used as the\n",
    "# input to the softmax function (which is basically a sigmoid\n",
    "# function extended to more than two classes). The more positive\n",
    "# a logit, the closer to one the corresponding probability that\n",
    "# is the output of the softmax.\n",
    "# https://en.wikipedia.org/wiki/Softmax_function\n",
    "\n",
    "def get_logits_img(features, n_classes, mode, params):\n",
    "    \"\"\"Computes logits for provided features.\n",
    "\n",
    "    Args:\n",
    "      features: A dictionary of tensors that are the features\n",
    "          and whose first dimension is batch (as returned by input_fn).\n",
    "      n_classes: Number of classes from which to predict (i.e. the number\n",
    "          of different values in the \"labels\" tensor returned by the\n",
    "          input_fn).\n",
    "      mode: A tf.estimator.ModeKeys.\n",
    "      params: Hyper parameters: \"convs\" specifying the configuration of the\n",
    "          convolutions, and \"hidden\" specifying the configuration of the\n",
    "          dense layers after the convolutions.\n",
    "\n",
    "    Returns:\n",
    "      The logits tensor with shape=[batch, n_classes].\n",
    "    \"\"\"\n",
    "    # The parameter \"convs\" specifies (kernel, stride, filters)\n",
    "    # of successive convolution layers.\n",
    "    convs = params.get('convs', ((10, 4, 32), (5, 4, 64)))\n",
    "    # The parameter \"hidden\" specifies the number of neurons of\n",
    "    # successive fully connected layers (after convolution).\n",
    "    hidden = params.get('hidden', (256,))\n",
    "    # The function tf.layers.conv2d expects the tensor to have format\n",
    "    # [batch, height, width, channels] -- since our \"img_64\" tensor\n",
    "    # has format [batch, height, width], we need to expand the tensor\n",
    "    # to get [batch, height, width, channels=1].\n",
    "    last_layer = tf.expand_dims(features['img_64'], axis=3)\n",
    "    # We start with dims=width=height=64 and filters=channels=1 and then\n",
    "    # successively reduce the number of dimensions while increasing the\n",
    "    # number of filters in every convolutional/maxpooling layer.\n",
    "    dim = 64\n",
    "    filters = 1\n",
    "    for kernel, stride, filters in convs:\n",
    "        conv = tf.layers.conv2d(\n",
    "            inputs=last_layer, filters=filters, kernel_size=[kernel, kernel],\n",
    "            padding='same', activation=tf.nn.relu)\n",
    "        last_layer = tf.layers.max_pooling2d(\n",
    "            inputs=conv, pool_size=[stride, stride], strides=stride)\n",
    "        dim /= stride\n",
    "    # \"Flatten\" the last layer to get shape [batch, *]\n",
    "    last_layer = tf.reshape(last_layer, [-1, filters * dim * dim])\n",
    "    # Add some fully connected layers.\n",
    "    for units in hidden:\n",
    "        dense = tf.layers.dense(inputs=last_layer, units=units,\n",
    "                                activation=tf.nn.relu)\n",
    "        # Regularize using dropout.\n",
    "        training = mode == tf.estimator.ModeKeys.TRAIN\n",
    "        last_layer = tf.layers.dropout(inputs=dense, rate=0.4,\n",
    "                                       training=training)\n",
    "    # Finally return logits that is activation of neurons in last layer.\n",
    "    return tf.layers.dense(inputs=last_layer, units=n_classes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing _derived/4_make_model_fn.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile _derived/4_make_model_fn.py\n",
    "# (Written into separate file for sharing with cloud code.)\n",
    "\n",
    "# Warning : Boilerplate code cell...\n",
    "\n",
    "# This cell defines a function that connects the \"get_logits_fn\"\n",
    "# to the estimator interface and adds some useful output for our problem.\n",
    "# By specifying different parameters, the same function can be reused\n",
    "# in section 5 where we use a recurrent neural network to compute the\n",
    "# logits.\n",
    "\n",
    "def make_model_fn(get_logits_fn, n_classes):\n",
    "    \"\"\"Creates a model_fn.\n",
    "\n",
    "    Args:\n",
    "      get_logits_fn: Function that computes logits from features.\n",
    "      n_classes: Number of classes.\n",
    "\n",
    "    Returns:\n",
    "      A model_fn to be used with an estimator.\n",
    "    \"\"\"\n",
    "\n",
    "    def model_fn(features, labels, mode, params):\n",
    "        \"\"\"The model_fn is passed as an argument to the estimator.\n",
    "\n",
    "        Args:\n",
    "          features: Dictionary mapping feature names to feature tensors.\n",
    "          labels: Optional labels (`None` during inference).\n",
    "          mode: A `tf.estimator.ModeKeys`.\n",
    "          params: Optional dictionary of hyper parameters.\n",
    "\n",
    "        Returns:\n",
    "          A `tf.estimator.EstimatorSpec`.\n",
    "        \"\"\"\n",
    "\n",
    "        # Create logits from features using RNN.\n",
    "        logits = get_logits_fn(features, n_classes=n_classes, mode=mode,\n",
    "                               params=params)\n",
    "\n",
    "        # Convert logits to probabilities.\n",
    "        probabilities = tf.nn.softmax(logits)\n",
    "        # Extract class with highest probability.\n",
    "        predictions = tf.argmax(probabilities, axis=1)\n",
    "\n",
    "        onehot_labels = loss = train_op = eval_metric_ops = None\n",
    "        if labels is not None:\n",
    "            onehot_labels = tf.one_hot(tf.squeeze(labels), n_classes)\n",
    "            loss = tf.losses.softmax_cross_entropy(onehot_labels, logits)\n",
    "\n",
    "        if mode == tf.estimator.ModeKeys.TRAIN:\n",
    "            # Compute loss.\n",
    "            global_step = tf.train.get_global_step()\n",
    "            # Minimize.\n",
    "            train_op = tf.train.AdamOptimizer().minimize(loss, global_step=global_step)\n",
    "            tf.summary.scalar('loss', loss)\n",
    "            # Output number of parameters for educational purposes.\n",
    "            trainable_params = 0\n",
    "            for var in tf.trainable_variables():\n",
    "                tf.logging.info('Variable \"%s\" : %s.', var.name,\n",
    "                                var.get_shape().as_list())\n",
    "                trainable_params += np.prod(var.get_shape().as_list())\n",
    "            tf.logging.info('Total params : %d.', trainable_params)\n",
    "\n",
    "        if mode == tf.estimator.ModeKeys.EVAL:\n",
    "            # Report accuracy when evaluating.\n",
    "            eval_metric_ops = {\n",
    "                'accuracy': tf.metrics.accuracy(labels, predictions),\n",
    "            }\n",
    "\n",
    "        return tf.estimator.EstimatorSpec(\n",
    "            loss=loss,\n",
    "            mode=mode,\n",
    "            predictions={\n",
    "                'probabilities': probabilities,\n",
    "                'predictions': predictions,\n",
    "            },\n",
    "            export_outputs={\n",
    "                'prediction': tf.estimator.export.PredictOutput(outputs={\n",
    "                    'probabilities': probabilities,\n",
    "                    'predictions': predictions,\n",
    "                }),\n",
    "            },\n",
    "            train_op=train_op,\n",
    "            eval_metric_ops=eval_metric_ops,\n",
    "        )\n",
    "\n",
    "    return model_fn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmpyQ_RzM\n",
      "INFO:tensorflow:Using config: {'_save_checkpoints_secs': 600, '_session_config': None, '_keep_checkpoint_max': 5, '_task_type': 'worker', '_is_chief': True, '_cluster_spec': <tensorflow.python.training.server_lib.ClusterSpec object at 0x7f7a4fc2eb90>, '_save_checkpoints_steps': None, '_keep_checkpoint_every_n_hours': 10000, '_service': None, '_num_ps_replicas': 0, '_tf_random_seed': None, '_master': '', '_num_worker_replicas': 1, '_task_id': 0, '_log_step_count_steps': 100, '_model_dir': '/tmp/tmpyQ_RzM', '_save_summary_steps': 10}\n",
      "WARNING:tensorflow:Estimator's model_fn (<function model_fn at 0x7f7a4fb2f668>) includes params argument, but params are not passed to Estimator.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f7a10599ad0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%run -i _derived/4_get_logits_img.py\n",
    "%run -i _derived/4_make_model_fn.py\n",
    "\n",
    "# Instead of creating a tf.estimator.LinearClassifier, we now create\n",
    "# a tf.estimator.Estimator and specify our custom model_fn that will\n",
    "# be used by the estimator to compute the predictions from the features.\n",
    "run_config = tf.estimator.RunConfig(save_summary_steps=10)\n",
    "model_fn = make_model_fn(get_logits_fn=get_logits_img, n_classes=len(classes))\n",
    "cnn_estimator = tf.estimator.Estimator(model_fn=model_fn, config=run_config)\n",
    "# -- from here on, the code is exactly the same as in the previous section ..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Variable \"conv2d/kernel:0\" : [10, 10, 1, 32].\n",
      "INFO:tensorflow:Variable \"conv2d/bias:0\" : [32].\n",
      "INFO:tensorflow:Variable \"conv2d_1/kernel:0\" : [5, 5, 32, 64].\n",
      "INFO:tensorflow:Variable \"conv2d_1/bias:0\" : [64].\n",
      "INFO:tensorflow:Variable \"dense/kernel:0\" : [1024, 256].\n",
      "INFO:tensorflow:Variable \"dense/bias:0\" : [256].\n",
      "INFO:tensorflow:Variable \"dense_1/kernel:0\" : [256, 10].\n",
      "INFO:tensorflow:Variable \"dense_1/bias:0\" : [10].\n",
      "INFO:tensorflow:Total params : 319466.\n",
      "INFO:tensorflow:Create CheckpointSaverHook.\n",
      "INFO:tensorflow:Saving checkpoints for 1 into /tmp/tmpyQ_RzM/model.ckpt.\n",
      "INFO:tensorflow:loss = 2.31637, step = 1\n",
      "INFO:tensorflow:Saving checkpoints for 100 into /tmp/tmpyQ_RzM/model.ckpt.\n",
      "INFO:tensorflow:Loss for final step: 0.990213.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.estimator.estimator.Estimator at 0x7f7a4fc2e310>"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Note: You can get detailed information about the training by pointing a\n",
    "# Tensorboard instance to the training directory.\n",
    "#\n",
    "# You start Tensorboard via a terminal, for example by clicking on \"New\"\n",
    "# -> \"Terminal\" in the Jupyter directory view (this also works when\n",
    "# Jupyter server is running inside a Docker image).\n",
    "#\n",
    "# The following command will start Tensorboard and show all experimental\n",
    "# data below the \"/tmp\" directory (which is where new model directories\n",
    "# are created if we do not specify a model path):\n",
    "#\n",
    "# $ tensorboard --logdir /tmp/\n",
    "#\n",
    "# And then view the Tensorboard in your browser:\n",
    "# http://localhost:6006\n",
    "\n",
    "# Bonus challenge (don't do this during the workshop) : Try to increase the\n",
    "# number of training steps, batch_size, and/or parameters (hint: check out\n",
    "# the \"params\" argument when creating the estimator in the previous cell)\n",
    "# -- what accuracy can achieve with this convolutional network?\n",
    "input_fn = make_input_fn('%s/train-*' % data_path, batch_size=100)\n",
    "cnn_estimator.train(input_fn=input_fn, steps=100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Starting evaluation at 2018-01-26-10:51:15\n",
      "INFO:tensorflow:Restoring parameters from /tmp/tmpyQ_RzM/model.ckpt-100\n",
      "INFO:tensorflow:Evaluation [1/1]\n",
      "INFO:tensorflow:Finished evaluation at 2018-01-26-10:51:17\n",
      "INFO:tensorflow:Saving dict for global step 100: accuracy = 0.78, global_step = 100, loss = 0.69352\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'accuracy': 0.77999997, 'global_step': 100, 'loss': 0.69351959}"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cnn_estimator.evaluate(input_fn=make_input_fn('%s/eval-*' % data_path),\n",
    "                       steps=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Restoring parameters from /tmp/tmpyQ_RzM/model.ckpt-100\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f7a1058a350>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "input_fn = make_input_fn('%s/test-*' % data_path, batch_size=1)\n",
    "show_predictions(cnn_estimator, predict_keys='probabilities')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4 Keras – bonus!<a name=\"_4 keras – bonus!\"></a><a name=\"_4 keras – bonus!\"></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\"Keras is a high-level neural networks API, written in Python and capable of\n",
    "running on top of TensorFlow, CNTK, or Theano. It was developed with a focus\n",
    "on enabling fast experimentation. Being able to go from idea to result with\n",
    "the least possible delay is key to doing good research.\"\n",
    "(from https://keras.io/)\n",
    "\n",
    "In this section we reconstruct the same convolutional model as in the last\n",
    "section using Keras and then convert the Keras model to an estimator so we\n",
    "can use the same input functions reading data in a streaming fashion from our\n",
    "data on disk.\n",
    "\n",
    "More useful links about Keras (not needed for this section):\n",
    "\n",
    "- https://keras.io/models/about-keras-models/\n",
    "- https://keras.io/models/sequential/\n",
    "- https://keras.io/models/model/\n",
    "- [Converting Keras model to Tensorflow estimator](https://cloud.google.com/blog/big-data/2017/12/new-in-tensorflow-14-converting-a-keras-model-to-a-tensorflow-estimator)\n",
    "- [Example using functional API with input_fn](https://github.com/keras-team/keras/blob/master/examples/mnist_tfrecord.py)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "firstlayer (Conv2D)          (None, 64, 64, 32)        3232      \n",
      "_________________________________________________________________\n",
      "max_pooling2d_1 (MaxPooling2 (None, 16, 16, 32)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_1 (Conv2D)            (None, 16, 16, 64)        51264     \n",
      "_________________________________________________________________\n",
      "max_pooling2d_2 (MaxPooling2 (None, 4, 4, 64)          0         \n",
      "_________________________________________________________________\n",
      "flatten_1 (Flatten)          (None, 1024)              0         \n",
      "_________________________________________________________________\n",
      "dense_1 (Dense)              (None, 256)               262400    \n",
      "_________________________________________________________________\n",
      "labels (Dense)               (None, 10)                2570      \n",
      "=================================================================\n",
      "Total params: 319,466\n",
      "Trainable params: 319,466\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "# Keras is part of Tensorflow\n",
    "from tensorflow import keras\n",
    "\n",
    "# The model is specified as a succession of layers.\n",
    "# The layers here specify exactly the same model as in the previous section.\n",
    "model = keras.models.Sequential([\n",
    "    # Note that we need to name the first layer (to match features, see next\n",
    "    # cell) and specify the input shape.\n",
    "    keras.layers.Conv2D(filters=32, kernel_size=10, activation='relu',\n",
    "                        padding='same', name='firstlayer',\n",
    "                        input_shape=(64, 64, 1)),\n",
    "    keras.layers.MaxPooling2D(pool_size=4),\n",
    "    keras.layers.Conv2D(filters=64, kernel_size=5, activation='relu',\n",
    "                        padding='same'),\n",
    "    keras.layers.MaxPooling2D(pool_size=4),\n",
    "    keras.layers.Flatten(),\n",
    "    keras.layers.Dense(units=256, activation='relu'),\n",
    "    # We also need to name the output layer.\n",
    "    tf.keras.layers.Dense(units=len(classes), activation='softmax',\n",
    "                          name='labels'),\n",
    "])\n",
    "model.compile(loss='categorical_crossentropy',\n",
    "              optimizer='adam',\n",
    "              metrics=['accuracy'])\n",
    "# Neat :-)\n",
    "model.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Using the Keras model from memory.\n",
      "INFO:tensorflow:Using default config.\n",
      "WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmp4YraC6\n",
      "INFO:tensorflow:Using config: {'_save_checkpoints_secs': 600, '_session_config': None, '_keep_checkpoint_max': 5, '_task_type': 'worker', '_is_chief': True, '_cluster_spec': <tensorflow.python.training.server_lib.ClusterSpec object at 0x7f7a280d50d0>, '_save_checkpoints_steps': None, '_keep_checkpoint_every_n_hours': 10000, '_service': None, '_num_ps_replicas': 0, '_tf_random_seed': None, '_master': '', '_num_worker_replicas': 1, '_task_id': 0, '_log_step_count_steps': 100, '_model_dir': '/tmp/tmp4YraC6', '_save_summary_steps': 100}\n",
      "INFO:tensorflow:Create CheckpointSaverHook.\n",
      "INFO:tensorflow:Restoring parameters from /tmp/tmp4YraC6/\n",
      "INFO:tensorflow:Saving checkpoints for 1 into /tmp/tmp4YraC6/model.ckpt.\n",
      "INFO:tensorflow:loss = 2.32024, step = 1\n",
      "INFO:tensorflow:Saving checkpoints for 100 into /tmp/tmp4YraC6/model.ckpt.\n",
      "INFO:tensorflow:Loss for final step: 0.887773.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.estimator.estimator.Estimator at 0x7f7a4ff8a550>"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Helper function to wrap input_fn().\n",
    "def keras_make_input_fn(*args, **kwargs):\n",
    "    def wrapper():\n",
    "        input_fn = make_input_fn(*args, **kwargs)\n",
    "        features, labels = input_fn()\n",
    "        # We need to specify which feature is used as the input to\n",
    "        # which layer. In our case we have a single input feature\n",
    "        # that is read by layer \"firstlayer\". Note that we also need\n",
    "        # to add an additional dimension to get shape=(64, 64, 1).\n",
    "        features = {\n",
    "            'firstlayer_input': tf.expand_dims(features['img_64'], axis=3),\n",
    "        }\n",
    "        # Labels are expected in one_hot format.\n",
    "        labels = tf.one_hot(tf.squeeze(labels), 10)\n",
    "        return features, labels\n",
    "    return wrapper\n",
    "\n",
    "# From now on we proceed exactly as before...\n",
    "keras_estimator = keras.estimator.model_to_estimator(model)\n",
    "input_fn = keras_make_input_fn('%s/train-*' % data_path, batch_size=100)\n",
    "keras_estimator.train(input_fn=input_fn, steps=100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Starting evaluation at 2018-01-26-10:52:20\n",
      "INFO:tensorflow:Restoring parameters from /tmp/tmp4YraC6/model.ckpt-100\n",
      "INFO:tensorflow:Evaluation [1/1]\n",
      "INFO:tensorflow:Finished evaluation at 2018-01-26-10:52:23\n",
      "INFO:tensorflow:Saving dict for global step 100: accuracy = 0.75, global_step = 100, loss = 0.730778\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'accuracy': 0.75, 'global_step': 100, 'loss': 0.73077756}"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "keras_estimator.evaluate(input_fn=keras_make_input_fn('%s/eval-*' % data_path),\n",
    "                         steps=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5 Recurrent neural network – bonus!\n",
    "\n",
    "So far we have been processing two dimensional image data. That\n",
    "data can be converted nicely into a dense tensor that is then the\n",
    "input layer of a linear classifier or convolutional network.\n",
    "\n",
    "If we want to process the raw stroke data, we need a different\n",
    "network architecture that reads in coordinate by coordinate, while\n",
    "updating its internal state and finally outputs a prediction --\n",
    "a **recurrent network**.\n",
    "\n",
    "Explaining the architecture for recurrent networks (specifically,\n",
    "we use a LSTM in this section) is out of the scope of this workshop,\n",
    "please read the following articles if you want to know more about the\n",
    "architecture:\n",
    "\n",
    "- http://colah.github.io/posts/2015-08-Understanding-LSTMs/\n",
    "- http://karpathy.github.io/2015/05/21/rnn-effectiveness/\n",
    "\n",
    "Using recurrent networks requires some tensor shape black magic and\n",
    "this is the main focus of this section."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "total 681M\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 eval-00000-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 eval-00001-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 eval-00002-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 eval-00003-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 eval-00004-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 eval-00005-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 eval-00006-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 eval-00007-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.7M Jan 26 10:47 eval-00008-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 eval-00009-of-00010\r\n",
      "-rw-r--r-- 1 root root   74 Jan 26 10:47 labels.txt\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 test-00000-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.7M Jan 26 10:47 test-00001-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 test-00002-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 test-00003-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 test-00004-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 test-00005-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 test-00006-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 test-00007-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 test-00008-of-00010\r\n",
      "-rw-r--r-- 1 root root 9.8M Jan 26 10:47 test-00009-of-00010\r\n",
      "-rw-r--r-- 1 root root  49M Jan 26 10:47 train-00000-of-00010\r\n",
      "-rw-r--r-- 1 root root  49M Jan 26 10:47 train-00001-of-00010\r\n",
      "-rw-r--r-- 1 root root  49M Jan 26 10:47 train-00002-of-00010\r\n",
      "-rw-r--r-- 1 root root  49M Jan 26 10:47 train-00003-of-00010\r\n",
      "-rw-r--r-- 1 root root  49M Jan 26 10:47 train-00004-of-00010\r\n",
      "-rw-r--r-- 1 root root  49M Jan 26 10:47 train-00005-of-00010\r\n",
      "-rw-r--r-- 1 root root  49M Jan 26 10:47 train-00006-of-00010\r\n",
      "-rw-r--r-- 1 root root  49M Jan 26 10:47 train-00007-of-00010\r\n",
      "-rw-r--r-- 1 root root  49M Jan 26 10:47 train-00008-of-00010\r\n",
      "-rw-r--r-- 1 root root  49M Jan 26 10:47 train-00009-of-00010\r\n"
     ]
    }
   ],
   "source": [
    "# Load stroke data generated in 1_qd_data (bonus section):\n",
    "stroke_data_path = '../data/dataset_stroke'\n",
    "!ls -l -h $stroke_data_path"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10 label classes:\n",
      "\n",
      "0 -> elephant\n",
      "1 -> giraffe\n",
      "2 -> kangaroo\n",
      "3 -> lion\n",
      "4 -> monkey\n",
      "5 -> panda\n",
      "6 -> penguin\n",
      "7 -> rhinoceros\n",
      "8 -> tiger\n",
      "9 -> zebra\n"
     ]
    }
   ],
   "source": [
    "# Label classes...\n",
    "classes = open('%s/labels.txt' % stroke_data_path).read().splitlines()\n",
    "print '%d label classes:\\n' % len(classes)\n",
    "for i, label in enumerate(classes):\n",
    "    print '%d -> %s' % (i, label)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SparseTensorValue(indices=array([[0, 0],\n",
      "       [1, 1],\n",
      "       [2, 2]]), values=array([1, 2, 3], dtype=int32), dense_shape=array([3, 3]))\n",
      "[[1 0 0]\n",
      " [0 2 0]\n",
      " [0 0 3]]\n"
     ]
    }
   ],
   "source": [
    "# Some remarks on \"sparse tensors\": Conceptually, these\n",
    "# tensors are an efficient representation of a tensor that\n",
    "# has mostly \"0\" values. In the example below we initialize\n",
    "# a sparse tensor by specifying only non-\"0\" values and then\n",
    "# print the tensor as well as its dense representation.\n",
    "\n",
    "# If we read \"variable length\" tensors from disk then these\n",
    "# tensors will also be represented as sparse tensors (although\n",
    "# the tensors might be \"dense\" in a sense that they have very\n",
    "# few \"0\" values).\n",
    "\n",
    "with tf.Graph().as_default():\n",
    "    # Only specify non-\"0\" values of 3x3 diagnal matrix.\n",
    "    sparse = tf.SparseTensor(indices=[[0, 0], [1, 1], [2, 2]], values=[1, 2, 3],\n",
    "                             dense_shape=[3, 3])\n",
    "    # Convert to dense representation. Note that we can specify any\n",
    "    # \"dense_shape\" (resulting dense tensor is zero padded).\n",
    "    dense = tf.sparse_to_dense(sparse.indices, sparse.dense_shape,\n",
    "                               sparse.values)\n",
    "    with tf.Session() as sess:\n",
    "        print sparse.eval()\n",
    "        print dense.eval()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing _derived/4_convert_sparse.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile _derived/4_convert_sparse.py\n",
    "# (Written into separate file for sharing with cloud code.)\n",
    "\n",
    "# Now let's define a helper function that limits variable length\n",
    "# sparse tensors to a maximum length and converts them to dense\n",
    "# tensors. We need to convert sparse tensors to dense tensors before\n",
    "# we can use them as input in the recurrent neural network.\n",
    "\n",
    "def convert_sparse(sparse, max_len):\n",
    "    \"\"\"Converts batched sparse tensor to dense tensor with specified size.\n",
    "    \n",
    "    Args:\n",
    "      sparse: tf.SparseTensor instance of shape=[n].\n",
    "      max_len: Truncates / zero-pads the dense tensor the specified max_len.\n",
    "    \"\"\"\n",
    "    # Convert to dense tensor.\n",
    "    dense = tf.sparse_to_dense(sparse.indices, sparse.dense_shape,\n",
    "                               sparse.values)\n",
    "    # Discard values above max_len.\n",
    "    dense = dense[:max_len]\n",
    "    # Zero-pad if length < max_len.\n",
    "    dense = tf.pad(dense, [[0, max_len - tf.shape(dense)[0]]])\n",
    "    return dense"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1 2 3]\n",
      "[1 2 3 4 5 0 0 0 0 0]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f7a4fa1ead0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%run -i _derived/4_convert_sparse.py\n",
    "\n",
    "with tf.Graph().as_default():\n",
    "    # Manually define sparse X-coordinates [1,2,3,4,5].\n",
    "    # Note that our stroke coordinate \"sparse tensors\" have a single dimension\n",
    "    # and do not contain any zeros at all...\n",
    "    stroke_x = tf.SparseTensor(\n",
    "        indices=[[0], [1], [2], [3], [4]],\n",
    "        values=[1, 2, 3, 4, 5],\n",
    "        dense_shape=[5])\n",
    "    # Extract both shorter and longer dense tensor.\n",
    "    dense_short = convert_sparse(stroke_x, max_len=3)\n",
    "    dense_long = convert_sparse(stroke_x, max_len=10)\n",
    "    with tf.Session() as sess:\n",
    "        print dense_short.eval()\n",
    "        print dense_long.eval()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing _derived/4_input_fn_stroke.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile _derived/4_input_fn_stroke.py\n",
    "# (Written into separate file for sharing with cloud code.)\n",
    "\n",
    "# Because the data is stored in a different format (strokes instead of pixels)\n",
    "# we need a new input_fn.\n",
    "\n",
    "# Maximum number of points in concatenated strokes.\n",
    "MAX_LEN = 256\n",
    "\n",
    "# Because every drawing has a different number of points, we use \"VarLenFeature\"\n",
    "# and not \"FixedLenFeature\" for the stroke data. This will create\n",
    "# \"SparseTensor\".\n",
    "feature_spec = {\n",
    "    'stroke_x': tf.VarLenFeature(dtype=tf.float32),\n",
    "    'stroke_y': tf.VarLenFeature(dtype=tf.float32),\n",
    "    'stroke_z': tf.VarLenFeature(dtype=tf.float32),\n",
    "    'stroke_len': tf.FixedLenFeature([], tf.int64),\n",
    "    'label': tf.FixedLenFeature([], tf.int64),\n",
    "}\n",
    "\n",
    "def parse_example_stroke(serialized_example):\n",
    "    features = tf.parse_single_example(serialized_example, feature_spec)\n",
    "    label = features.pop('label')\n",
    "\n",
    "    # The we create a 'stroke' tensor with shape [3, MAX_LEN] where the first\n",
    "    # dimension indicates whether the values are X, Y, or Z coordinates.\n",
    "    stroke = tf.stack([\n",
    "        convert_sparse(features['stroke_x'], max_len=MAX_LEN),\n",
    "        convert_sparse(features['stroke_y'], max_len=MAX_LEN),\n",
    "        convert_sparse(features['stroke_z'], max_len=MAX_LEN),\n",
    "    ])\n",
    "\n",
    "    # Also truncate the \"stroke_len\" to MAX_LEN if needed.\n",
    "    stroke_len = tf.minimum(tf.cast(MAX_LEN, tf.int64), features['stroke_len'])\n",
    "\n",
    "    return dict(stroke=stroke, stroke_len=stroke_len), label\n",
    "\n",
    "# Copied from above Section \"1.2 Reading the data using Tensorflow\"\n",
    "def make_input_fn_stroke(files_pattern, batch_size=100):\n",
    "    def input_fn():\n",
    "        dataset = tf.data.TFRecordDataset(tf.gfile.Glob(files_pattern))\n",
    "        dataset = dataset.map(parse_example_stroke).batch(batch_size)\n",
    "        dataset = dataset.shuffle(buffer_size=5*batch_size).repeat()\n",
    "        features, labels = dataset.make_one_shot_iterator().get_next()\n",
    "        return features, labels\n",
    "    return input_fn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f7a103f8350>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Read some examples with above input_fn and plot data.\n",
    "\n",
    "%run -i _derived/4_input_fn_stroke.py\n",
    "\n",
    "# (Modified code from section 1.1 and 1.2)\n",
    "\n",
    "def show_stroke_img(stroke, label, ax=None):\n",
    "    \"\"\"Plots stroke data.\n",
    "\n",
    "    Args:\n",
    "      stroke: Array of shape=[3, n] where the second dimension\n",
    "          is time and the first dimension indicates X/Y coordinates\n",
    "          and Z-dimension that is set to 1 when a stroke ends and\n",
    "          0 otherwise (the array actually represents an array of\n",
    "          concatenated strokes and the Z-dimension is needed to tell\n",
    "          the individual strokes apart).\n",
    "    \"\"\"\n",
    "    ax = ax if ax else pyplot.gca()\n",
    "    xy = stroke[:2, :].cumsum(axis=1)\n",
    "    ax.plot(*xy)\n",
    "    # Plot all the strokes, including connecting line between strokes.\n",
    "    pxy = xy[:, stroke[2] != 0]\n",
    "    # Red dots mark end of individual strokes.\n",
    "    ax.plot(pxy[0], pxy[1], 'ro')\n",
    "    ax.set_xticks([])\n",
    "    ax.set_yticks([])\n",
    "    ax.set_title(label)\n",
    "\n",
    "batch_size = 5\n",
    "input_fn = make_input_fn_stroke(files_pattern='%s/train-*' % stroke_data_path,\n",
    "                                batch_size=batch_size)\n",
    "with tf.Graph().as_default():\n",
    "    features, labels = input_fn()\n",
    "    with tf.train.MonitoredSession() as sess:\n",
    "        strokes_, labels_ = sess.run([features['stroke'], labels])\n",
    "        pyplot.figure(figsize=(10, 2))\n",
    "        for i in range(batch_size):\n",
    "            ax = pyplot.subplot(1, batch_size, i+1)\n",
    "            show_stroke_img(strokes_[i], classes[labels_[i]], ax)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing _derived/4_get_nth.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile _derived/4_get_nth.py\n",
    "# (Written into separate file for sharing with cloud code.)\n",
    "\n",
    "# Another helper function: This function will return the \"nth element\"\n",
    "# with different \"n\" for every element in the batch.\n",
    "# We will later need this function to get the prediction in the output\n",
    "# of the dynamic_rnn.\n",
    "\n",
    "def get_nth(tensor, ns, last_dim):\n",
    "    \"\"\"Tensor has shape [batch_size, max_len, last_dim].\"\"\"\n",
    "    shape = tf.shape(tensor)\n",
    "    batch_size, max_len = shape[0], shape[1]\n",
    "    # Flatten first two dimensions.\n",
    "    tensor = tf.reshape(tensor, [-1, last_dim])\n",
    "    # Calculate indices within flattened tensor.\n",
    "    idxs = tf.range(0, batch_size) * max_len + (tf.cast(ns, tf.int32) - 1)\n",
    "    # Return nth elements.\n",
    "    # TODO get rid of error UserWarning : https://stackoverflow.com/questions/35892412\n",
    "    return tf.gather(tensor, idxs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 2  2]\n",
      " [-3 -3]]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f7a0420b210>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%run -i _derived/4_get_nth.py\n",
    "\n",
    "with tf.Graph().as_default(), tf.Session(graph=tf.Graph()) as sess:\n",
    "    # Define a batch with shape=[batch_size=2, max_len=5, last_dim=2]\n",
    "    batch = tf.constant([\n",
    "        # First tensor in batch (shape=[5, 2])\n",
    "        [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]],\n",
    "        # Second tensor in batch (shape=[5, 2])\n",
    "        [[-1, -1], [-2, -2], [-3, -3], [-4, -4], [-5, -5]],\n",
    "    ])\n",
    "    # Return the second and third element.\n",
    "    lens = tf.constant([2, 3], dtype=tf.int64)\n",
    "    print get_nth(batch, lens, last_dim=2).eval()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing _derived/4_get_logits_stroke.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile _derived/4_get_logits_stroke.py\n",
    "# (Written into separate file for sharing with cloud code.)\n",
    "\n",
    "# This function creates creates the logits fro the stroke features.\n",
    "def get_logits_stroke(features, n_classes, mode, params):\n",
    "    \"\"\"Computes logits for provided features.\n",
    "\n",
    "    Args:\n",
    "      features: A dictionary of tensors that are the features\n",
    "          and whose first dimension is batch (as returned by input_fn).\n",
    "      n_classes: Number of classes from which to predict (i.e. the number\n",
    "          of different values in the \"labels\" tensor returned by the\n",
    "          input_fn).\n",
    "      mode: A tf.estimator.ModeKeys.\n",
    "      params: Hyper parameters: \"cell_size\" specifying the state size of\n",
    "          the LSTM cells, and \"hidden\" specifying the configuration of the\n",
    "          dense layers after recurrent network.\n",
    "\n",
    "    Returns:\n",
    "      The logits tensor with shape=[batch, n_classes].\n",
    "    \"\"\"\n",
    "\n",
    "    cell_size = params.get('cell_size', 256)\n",
    "    hidden = params.get('hidden', ())\n",
    "    \n",
    "    # First we convert our data from \"coords major\" to \"time major\",\n",
    "    # as required by the dynamic_rnn API.\n",
    "    # [batch, coords, time] -> [batch, time, coords]\n",
    "    stroke = tf.transpose(features['stroke'], perm=[0, 2, 1])\n",
    "    stroke_len = features['stroke_len']\n",
    "\n",
    "    # Construct a bi-directional dynamic recurrent NN with LSTM\n",
    "    # cells.\n",
    "    outputs, states = tf.nn.bidirectional_dynamic_rnn(\n",
    "            cell_fw=tf.nn.rnn_cell.LSTMCell(cell_size),\n",
    "            cell_bw=tf.nn.rnn_cell.LSTMCell(cell_size),\n",
    "            inputs=stroke,\n",
    "            sequence_length=stroke_len,\n",
    "            dtype=tf.float32,\n",
    "    )\n",
    "    # Use helper function from last cell to extract RNN output values.\n",
    "    outputs = tf.concat((get_nth(outputs[0], stroke_len, last_dim=cell_size),\n",
    "                         get_nth(outputs[1], stroke_len, last_dim=cell_size)),\n",
    "                        axis=1)\n",
    "\n",
    "    # Add fully connected layers on top.\n",
    "    for units in hidden:\n",
    "        outputs = tf.layers.dense(inputs=outputs, units=units,\n",
    "                                  activation=tf.nn.relu)\n",
    "\n",
    "    # Logits are activations of last fully connected layer.\n",
    "    return tf.layers.dense(inputs=outputs, units=n_classes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmpDeOLY1\n",
      "INFO:tensorflow:Using config: {'_save_checkpoints_secs': 600, '_session_config': None, '_keep_checkpoint_max': 5, '_task_type': 'worker', '_is_chief': True, '_cluster_spec': <tensorflow.python.training.server_lib.ClusterSpec object at 0x7f7a0420b690>, '_save_checkpoints_steps': None, '_keep_checkpoint_every_n_hours': 10000, '_service': None, '_num_ps_replicas': 0, '_tf_random_seed': None, '_master': '', '_num_worker_replicas': 1, '_task_id': 0, '_log_step_count_steps': 100, '_model_dir': '/tmp/tmpDeOLY1', '_save_summary_steps': 1}\n",
      "WARNING:tensorflow:Estimator's model_fn (<function model_fn at 0x7f7a4b7c46e0>) includes params argument, but params are not passed to Estimator.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python2.7/dist-packages/tensorflow/python/ops/gradients_impl.py:96: UserWarning: Converting sparse IndexedSlices to a dense Tensor of unknown shape. This may consume a large amount of memory.\n",
      "  \"Converting sparse IndexedSlices to a dense Tensor of unknown shape. \"\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Variable \"bidirectional_rnn/fw/lstm_cell/kernel:0\" : [259, 1024].\n",
      "INFO:tensorflow:Variable \"bidirectional_rnn/fw/lstm_cell/bias:0\" : [1024].\n",
      "INFO:tensorflow:Variable \"bidirectional_rnn/bw/lstm_cell/kernel:0\" : [259, 1024].\n",
      "INFO:tensorflow:Variable \"bidirectional_rnn/bw/lstm_cell/bias:0\" : [1024].\n",
      "INFO:tensorflow:Variable \"dense/kernel:0\" : [512, 10].\n",
      "INFO:tensorflow:Variable \"dense/bias:0\" : [10].\n",
      "INFO:tensorflow:Total params : 537610.\n",
      "INFO:tensorflow:Create CheckpointSaverHook.\n",
      "INFO:tensorflow:Saving checkpoints for 1 into /tmp/tmpDeOLY1/model.ckpt.\n",
      "INFO:tensorflow:loss = 2.30112, step = 1\n",
      "INFO:tensorflow:Saving checkpoints for 100 into /tmp/tmpDeOLY1/model.ckpt.\n",
      "INFO:tensorflow:Loss for final step: 2.26237.\n",
      "INFO:tensorflow:Starting evaluation at 2018-01-26-10:53:23\n",
      "INFO:tensorflow:Restoring parameters from /tmp/tmpDeOLY1/model.ckpt-100\n",
      "INFO:tensorflow:Evaluation [1/10]\n",
      "INFO:tensorflow:Evaluation [2/10]\n",
      "INFO:tensorflow:Evaluation [3/10]\n",
      "INFO:tensorflow:Evaluation [4/10]\n",
      "INFO:tensorflow:Evaluation [5/10]\n",
      "INFO:tensorflow:Evaluation [6/10]\n",
      "INFO:tensorflow:Evaluation [7/10]\n",
      "INFO:tensorflow:Evaluation [8/10]\n",
      "INFO:tensorflow:Evaluation [9/10]\n",
      "INFO:tensorflow:Evaluation [10/10]\n",
      "INFO:tensorflow:Finished evaluation at 2018-01-26-10:53:25\n",
      "INFO:tensorflow:Saving dict for global step 100: accuracy = 0.15, global_step = 100, loss = 2.19217\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'accuracy': 0.15000001, 'global_step': 100, 'loss': 2.1921659}"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%run -i _derived/4_get_logits_stroke.py\n",
    "\n",
    "# Same as before : Create estimator, train, evaluate.\n",
    "# Note that we need *much* more time and/or CPU power to get\n",
    "# decent results with the RNN, but eventually it will outperform\n",
    "# the convolutional classifier...\n",
    "\n",
    "# Use make_model_fn from section \"3 Custom convolutional classifier\"\n",
    "model_fn = make_model_fn(get_logits_fn=get_logits_stroke, n_classes=len(classes))\n",
    "config = tf.estimator.RunConfig(save_summary_steps=1)\n",
    "rnn_estimator = tf.estimator.Estimator(model_fn=model_fn, config=config)\n",
    "\n",
    "# We use smaller batch size to keep memory usage below ~1G (the\n",
    "# RNN has a large memory footprint because of the temporal unrolling).\n",
    "# To get good performance with this type of network we need much longer\n",
    "# training, which is impractical in a notebook. See next notebook\n",
    "# \"5_qd_cloud\" that describes how to train the network on Google Cloud\n",
    "# Environment using Cloud ML...\n",
    "input_fn = make_input_fn_stroke('%s/train-*' % stroke_data_path, batch_size=10)\n",
    "rnn_estimator.train(input_fn=input_fn, steps=100)\n",
    "input_fn = make_input_fn_stroke('%s/eval-*' % stroke_data_path, batch_size=10)\n",
    "rnn_estimator.evaluate(input_fn=input_fn, steps=10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# A References\n",
    "\n",
    "- https://www.tensorflow.org/versions/master/get_started/feature_columns\n",
    "- https://arxiv.org/abs/1704.03477 : A Neural Representation of Sketch Drawings\n",
    "- https://cloud.google.com/blog/big-data/2017/01/learn-tensorflow-and-deep-learning-without-a-phd : Nice tutorial explaining convolutions, recurrent networks, and some deep learning tricks – Challenge: try to improve the models in this notebook with the techniques described in this presentation!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
